// h2socket.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "h2server.h"
#include "h2log.h"


// ============================================================================
// Listen with IP & port specified by set_listen_ip() & set_listen_port()
// ============================================================================
bool h2server::listen()
// return: <0> succeed, <-1> failed
{
    close();
	_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (_socket == INVALID_SOCKET)
	{
		LOG::error(ERR_CODE_SYSTEM, __FUNCTION__"() Error calling socket(). (errorcode=%ld)", WSAGetLastError());
	    return false;
	}

	sockaddr_in service;
	service.sin_family = AF_INET;
	service.sin_addr.s_addr = _ip;
	service.sin_port = htons(_port);

	if (SOCKET_ERROR == ::bind(_socket, (SOCKADDR*) &service, sizeof(service)))
	{
		LOG::error(ERR_CODE_SYSTEM, __FUNCTION__"() Error calling bind(). (SOCKET=%d, IP=%d.%d.%d.%d PORT=%d)", _socket,
			_ip&0xFF, (_ip>>8)&0xFF, (_ip>>16)&0xFF, (_ip>>24)&0xFF, _port);
		closesocket(_socket);
		_socket = INVALID_SOCKET;
		return false;
	}

	if (SOCKET_ERROR == ::listen( _socket, SOMAXCONN ))
	{
		LOG::error(ERR_CODE_SYSTEM, __FUNCTION__ " Error calling listen(). (SOCKET=%d, IP=%d.%d.%d.%d PORT=%d)", _socket,
			_ip&0xFF, (_ip>>8)&0xFF, (_ip>>16)&0xFF, (_ip>>24)&0xFF, _port);
		return false;
	}

	LOG::debug(DEBUG_SOCKET | DEBUG_L2, __FUNCTION__"() Socket listened. (SOCKET=%d, IP=%d.%d.%d.%d PORT=%d)", _socket,
		_ip&0xFF, (_ip>>8)&0xFF, (_ip>>16)&0xFF, (_ip>>24)&0xFF, _port);
	return true;
}


// ============================================================================
// Close listen socket
// ============================================================================
void h2server::close()
// return: <0> succeed, <-1> failed
{
	if(INVALID_SOCKET == _socket)
	{
		LOG::debug(DEBUG_SOCKET | DEBUG_L3, __FUNCTION__"() Listen socket not initlized.");
		return;
	}

	::closesocket(_socket);
	_socket = INVALID_SOCKET;
	LOG::debug(DEBUG_SOCKET | DEBUG_L3, __FUNCTION__"() Listen socket %d closed.", _socket);
}




// ============================================================================
// Accept new connection to listen socket
// ============================================================================
bool h2server::accept_ready(int wait_ms)
{
	fd_set rset;
	timeval tv;

	FD_ZERO(&rset);
	FD_SET(_socket, &rset);
	tv.tv_sec = wait_ms / 1000;
	tv.tv_usec = wait_ms % 1000;

	int iRet = select(_socket + 1, &rset, NULL, NULL, &tv);

	// select() failed
	if(SOCKET_ERROR == iRet)
	{
		LOG::error(ERR_CODE_SYSTEM, __FUNCTION__"() Select failed: %d", WSAGetLastError());
		return false;
	}

	return 0 != iRet;
}

// ============================================================================
// Accept new connection to listen socket
// ============================================================================
bool h2server::accept(h2socket *ph2socket)
// [ph2socket]: <NULL> check connect queue only, <&h2socket> new connection instance
// return: <0> new connection arrived, <1> no new connection, <-1> failed 
{
    if(!accept_ready(0) || !ph2socket)
    {
        return false;
    }

	SOCKET hSocket;

	// new connection in the queue, accept the connection.
	hSocket = ::accept(_socket, NULL, NULL);
	if (INVALID_SOCKET == hSocket)
	{
		LOG::error(ERR_CODE_SYSTEM, __FUNCTION__"() Accept failed: %d", WSAGetLastError());
		return false;
	}

    if(!ph2socket->attach_socket(hSocket))
	{
		return false;
	}

    LOG::debug(DEBUG_SOCKET | DEBUG_L3, __FUNCTION__"() Client connected. (SOCKET=%d)", hSocket);
	return true;
}